package cn.hutool.postfix.generator;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.hutool.postfix.generator.common.Config;
import cn.hutool.postfix.generator.common.Watch;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * 入口 类
 *
 * @author liuxiongkang
 */
public class Main {
    private static final Log LOG = LogFactory.get(Main.class);

    private Main() {
        //do nothing
    }

    public static void main(String[] args) {
        // 1 查询 hutool 项目下 所有 pom.xml , 再排除指定的 pom
        Watch.start("查询 hutool 项目下 所有 pom.xml");
        List<File> pomFileList = getPomFileList();
        if (pomFileList.isEmpty()) {
            LOG.warn("配置目录下未找到可用hutool模块");
            return;
        }
        // 2 获取 hutool 下所有的 util class, 按照前 3个包名分组( = cn.hutool.xxx )  ;   Hutool.getAllUtils() 需要指定 <optional>true</optional> 的依赖,否则会丢失 UtilClass
        Watch.start("获取 hutool 下所有的 util class");
        List<String> utilClass = scanUtilClass();
        Map<String, List<String>> utilClassMap = utilClass.stream().collect(Collectors.groupingBy(name -> name.substring(0, CharSequenceUtil.ordinalIndexOf(name, ".", 3))));
        // 3 收集每个模块的util信息, 生成规则文件,  (每个pom 相当于一个hutool 模块, 每个pom 使用 mvn dependency:build-classpath 命令生成 包含依赖路径的文件)
        Watch.start("收集每个模块的util信息, 生成规则文件");
        FileUtil.del(RuleDocletGenerator.TEMP_PATH);
        pomFileList.forEach(pomFile -> RuleDocletGenerator.collectBuildInfo(pomFile, utilClassMap));
        // 4 拿到 hutool 当前版本号, 重命名文件带上版本号
        Watch.start("拿到 hutool 当前版本号, 重命名文件带上版本号");
        renameWithVersion(pomFileList);
        // 5 根据支持的 lang 分别生成 hutool-all.postfixTemplates , 且复制到 指定 idea-plugin  位置;  hutool-all 不带版本号, 防止复制到 idea目录下,造成多个不同版本的 重复文件
        Watch.start("生成 hutool-all.postfixTemplates");
        RuleDocletGenerator.generateAllRuleFile();
        // 6
        Watch.start("统计冲突总数");
        RuleOverrides.overrideRules();
        // 7
        Watch.start("统计规则总数");
        RuleCounter.countHutoolAll();
        // 8 打印耗时
        LOG.info(Watch.prettyPrint("生成 hutool-postfix-templates"));
    }

    /**
     * 过滤出 src\\main\\java目录下的 .java文件, 将每个 java文件转成 类名形式 如: cn.hutool.extra.spring.SpringUtil.java
     */
    private static List<String> scanUtilClass() {
        return FileUtil.loopFiles(Config.SOURCE_PATH, f -> f.isFile() && f.getName().endsWith(".java") && f.getAbsolutePath().contains("src\\main\\java")).stream()//
                .map(f -> f.getAbsolutePath().replace("\\", String.valueOf(StrPool.C_SLASH)))//
                .map(p -> ReUtil.getGroup1("src/main/java/(.*)", p).replace(String.valueOf(StrPool.C_SLASH), "."))//
                .filter(name -> name.endsWith("Util.java") || CollUtil.contains(Config.EXT_CLASS_INCLUDES, name) || CollUtil.contains(Config.EXT_CLASS_INCLUDES, name.replace(".java", "")))//
                .collect(Collectors.toList());
    }

    /**
     * 将规则文件重命名 带上版本号
     */
    private static void renameWithVersion(List<File> pomFileList) {
        Optional<String> versionOpt = pomFileList.stream().filter(file -> !"hutool".equals(file.getParentFile().getName())).findFirst()// 找到一个非hutool的pom文件
                .map(file -> XmlUtil.xmlToMap(FileUtil.readUtf8String(file)))// 读取xml 成 map
                .map(map -> BeanUtil.getProperty(map, "parent.version"));// 取到版本
        versionOpt.ifPresent(version -> FileUtil.loopFiles(RuleDocletGenerator.TEMP_PATH).// 获取到所有 规则文件
                forEach(file -> FileUtil.rename(file, FileUtil.mainName(file.getName()).concat("-").concat(version), true, true)));// 重命名
    }

    /**
     * 判断是否过滤模块
     */
    private static boolean isFilterPomFile(File file) {
        String componentName = file.getParentFile().getName();
        if (!Config.INCLUDES_COMPONENT.isEmpty()) {
            return !CollUtil.contains(Config.INCLUDES_COMPONENT, componentName);
        }
        return CollUtil.contains(Config.EXCLUDES_COMPONENT, componentName);
    }

    private static List<File> getPomFileList() {
        return FileUtil.loopFiles(Config.SOURCE_PATH, file -> "pom.xml".equals(file.getName()) && !isFilterPomFile(file));
    }
}
